feat(i2c): Add i2c support and stabilize c_board transfer#41
feat(i2c): Add i2c support and stabilize c_board transfer#41gqsdjhh wants to merge 4 commits intoAlliance-Algorithm:mainfrom
Conversation
Walkthrough添加对 I2C 的端到端支持:数据结构与枚举扩展、协议序列化/反序列化钩子、主机端/固件端 I2C 报文构建与路由、STM32/HPM 平台的 I2C 驱动与中断/DMA 集成,以及相关构建配置与子模块更新(GPIO/IRQ/DMA 配置、CubeMX 项目调整)。 Changes
Sequence Diagram(s)sequenceDiagram
participant Host as Host
participant Serializer as Serializer
participant USB as USB
participant Deserializer as Deserializer
participant Handler as Handler
participant I2Cdrv as I2C Driver
participant HAL as HAL
Host->>Serializer: write_i2c_write(kI2c0, data)
Serializer->>USB: send bytes
USB->>Deserializer: receive bytes
Deserializer->>Deserializer: process_i2c_field()
Deserializer->>Handler: i2c_write_deserialized_callback()
Handler->>I2Cdrv: handle_downlink_write()
I2Cdrv->>HAL: start DMA Tx/Rx or Mem_Read/Mem_Write
HAL-->>I2Cdrv: completion or error
alt success
I2Cdrv->>Handler: complete
else error
I2Cdrv->>Serializer: write_i2c_error()
Serializer->>USB: send error
end
估算代码审查工作量🎯 4 (复杂) | ⏱️ ~50 分钟 可能相关的PR
诗歌
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (3)
firmware/c_board/app/src/app.cpp (1)
86-86: 建议明确 Line 86 的 no-op 调用意图。Line 86 当前调用
i2c::i2c0->update(),但firmware/c_board/app/src/i2c/i2c.hpp的update()为空实现。建议加注释说明“保留扩展点”,避免后续维护误判。可选改进示例
- i2c::i2c0->update(); + // Keep this call as an extension point for future non-blocking I2C work. + i2c::i2c0->update();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@firmware/c_board/app/src/app.cpp` at line 86, 在调用 i2c::i2c0->update() 的位置(当前在 app.cpp 第 86 行)明确说明这是一处空实现的保留扩展点:在调用旁边添加注释,注明 i2c::I2C::update() 在 firmware/c_board/app/src/i2c/i2c.hpp 中目前为空实现、用于未来扩展或平台特定实现,并指明不要删除或误以为是忘写逻辑;同时报错/断言等不可行时保持调用以便兼容未来实现。host/include/librmcs/agent/rmcs_board.hpp (1)
75-85: 考虑添加[[unlikely]]属性以保持一致性。
c_board.hpp中的i2c0_write和i2c0_read方法在错误条件上使用了[[unlikely]]属性,但此文件缺少该属性。建议保持两个 board 实现的一致性。♻️ 建议的修改
PacketBuilder& i2c0_write(const librmcs::data::I2cDataView& data) { - if (data.payload.empty() || !builder_.write_i2c(data::DataId::kI2c0, data)) + if (data.payload.empty() || !builder_.write_i2c(data::DataId::kI2c0, data)) [[unlikely]] throw std::invalid_argument{"I2C0 write failed: Invalid I2C data"}; return *this; } PacketBuilder& i2c0_read(const librmcs::data::I2cReadConfigView& data) { - if (data.read_length == 0 || !builder_.write_i2c_read_config(data::DataId::kI2c0, data)) + if (data.read_length == 0 || !builder_.write_i2c_read_config(data::DataId::kI2c0, data)) + [[unlikely]] throw std::invalid_argument{"I2C0 read failed: Invalid I2C read config"}; return *this; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@host/include/librmcs/agent/rmcs_board.hpp` around lines 75 - 85, The error branches in PacketBuilder::i2c0_write and PacketBuilder::i2c0_read lack the [[unlikely]] attribute used in the c_board.hpp equivalent; update the conditional checks so the throw paths are annotated with [[unlikely]] (i.e., mark the failing condition as unlikely when checking data.payload.empty() or data.read_length == 0 and the builder_.write_* calls) to match the other board implementation and maintain consistency between i2c0_write and i2c0_read error branches.firmware/c_board/app/src/i2c/i2c.hpp (1)
28-40: 空 payload 静默返回 vs 发送错误通知 - 建议对齐行为。当
payload.empty()时,此方法静默返回(line 29-30),而当payload.size() > kMaxDataLength时发送错误通知(line 32-35)。对比
rmcs_board版本的handle_downlink_write(同样静默返回),两者行为一致。但从协议角度,主机端可能期望收到反馈。如果这是预期行为(空写入不是错误),可以添加注释说明设计意图。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@firmware/c_board/app/src/i2c/i2c.hpp` around lines 28 - 40, The function handle_downlink_write currently returns silently when data.payload.empty() but sends an error when payload.size() > kMaxDataLength, causing inconsistent host feedback; update handle_downlink_write to either call write_error(data.slave_address) for empty payloads to align behavior with oversized payloads, or add a clear code comment above the empty-payload check explaining that an empty write is intentionally treated as a no-op and not an error (and reference write_error, kMaxDataLength, and write_blocking in the comment to clarify the contract).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@firmware/c_board/bsp/cubemx/Core/Src/i2c.c`:
- Around line 85-88: 当前在 i2c.c 中对 I2C2 事件/错误中断使用 HAL_NVIC_SetPriority(..., 0, 0)
并启用 IRQ (HAL_NVIC_EnableIRQ(I2C2_EV_IRQn)/HAL_NVIC_EnableIRQ(I2C2_ER_IRQn)),会与
USB (OTG_FS_IRQn) 冲突且你的传输调用 HAL_I2C_Master_Transmit()/HAL_I2C_Master_Receive()
是阻塞式,不需要中断;请在配置中将 I2C2_EV_IRQn 和 I2C2_ER_IRQn 的优先级改为 HAL_NVIC_SetPriority(...,
1, 0) 或直接移除/注释掉对应的 HAL_NVIC_EnableIRQ 调用(如果不需要错误中断处理),并确保修改发生在配置 I2C2
中断的初始化函数(查找 HAL_NVIC_SetPriority / HAL_NVIC_EnableIRQ 对 I2C2 的调用处)。
In `@firmware/rmcs_board/app/src/i2c/i2c.hpp`:
- Around line 58-60: handle_downlink_read_config currently silently returns when
data.read_length is 0 or > kMaxDataLength; change this to send the same I2C
error notification the c_board implementation uses instead of silent return. In
the handle_downlink_read_config function (and when validating
data::I2cReadConfigView.read_length against kMaxDataLength) emit the I2C error
notification/response (use the project’s existing notify/send I2C error API used
by the c_board codebase) with a descriptive error reason before returning so the
host receives the error feedback.
- Around line 32-56: handle_downlink_write currently omits a payload size limit
check: before using data.payload in i2c_master_write/i2c_master_address_write
validate that data.payload.size() <= kMaxDataLength (same limit used in
c_board), and if it exceeds, call
usb::get_serializer().write_i2c_error(data_id_, data.slave_address) (assert on
non-kInvalidArgument like existing error paths) and return; reference symbols:
handle_downlink_write, data::I2cDataView, kMaxDataLength, write_i2c_error,
data_id_.
In `@firmware/rmcs_board/bsp/hpm_sdk`:
- Line 1: 回退移除了 GPIO 初始化(board_init_gpio_pins)和 Flash/XPI 配置,可能破坏依赖于 GPIO 状态的
HAL I2C 传输;请在 PR 中补充回退原因与兼容性评估、并在代码里恢复或有条件调用
board_init_gpio_pins()(或提供替代初始化路径)以保证 I2C 初始化/传输和中断处理行为一致;在本地或 CI 运行覆盖性验证:1) 验证
HAL I2C init/transfer 在无 board_init_gpio_pins 情况下是否失败;2) 验证中断和 I/O 线路的电平/复用设置;3)
恢复或迁移 Flash/XPI NOR board 配置并记录任何行为差异;在 PR 描述中列出对比 a82a5354 与 132349c9
的差异、测试结果和最终决定(恢复、条件初始化或明确不兼容)。
---
Nitpick comments:
In `@firmware/c_board/app/src/app.cpp`:
- Line 86: 在调用 i2c::i2c0->update() 的位置(当前在 app.cpp 第 86
行)明确说明这是一处空实现的保留扩展点:在调用旁边添加注释,注明 i2c::I2C::update() 在
firmware/c_board/app/src/i2c/i2c.hpp
中目前为空实现、用于未来扩展或平台特定实现,并指明不要删除或误以为是忘写逻辑;同时报错/断言等不可行时保持调用以便兼容未来实现。
In `@firmware/c_board/app/src/i2c/i2c.hpp`:
- Around line 28-40: The function handle_downlink_write currently returns
silently when data.payload.empty() but sends an error when payload.size() >
kMaxDataLength, causing inconsistent host feedback; update handle_downlink_write
to either call write_error(data.slave_address) for empty payloads to align
behavior with oversized payloads, or add a clear code comment above the
empty-payload check explaining that an empty write is intentionally treated as a
no-op and not an error (and reference write_error, kMaxDataLength, and
write_blocking in the comment to clarify the contract).
In `@host/include/librmcs/agent/rmcs_board.hpp`:
- Around line 75-85: The error branches in PacketBuilder::i2c0_write and
PacketBuilder::i2c0_read lack the [[unlikely]] attribute used in the c_board.hpp
equivalent; update the conditional checks so the throw paths are annotated with
[[unlikely]] (i.e., mark the failing condition as unlikely when checking
data.payload.empty() or data.read_length == 0 and the builder_.write_* calls) to
match the other board implementation and maintain consistency between i2c0_write
and i2c0_read error branches.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 99e630ac-7991-4c1f-89de-584c2a600765
📒 Files selected for processing (26)
.codexcore/include/librmcs/data/datas.hppcore/src/protocol/deserializer.cppcore/src/protocol/deserializer.hppcore/src/protocol/protocol.hppcore/src/protocol/serializer.hppfirmware/c_board/app/src/app.cppfirmware/c_board/app/src/i2c/i2c.hppfirmware/c_board/app/src/usb/vendor.hppfirmware/c_board/bsp/cubemx/Core/Inc/i2c.hfirmware/c_board/bsp/cubemx/Core/Inc/stm32f4xx_hal_conf.hfirmware/c_board/bsp/cubemx/Core/Inc/stm32f4xx_it.hfirmware/c_board/bsp/cubemx/Core/Src/gpio.cfirmware/c_board/bsp/cubemx/Core/Src/i2c.cfirmware/c_board/bsp/cubemx/Core/Src/main.cfirmware/c_board/bsp/cubemx/Core/Src/stm32f4xx_it.cfirmware/c_board/bsp/cubemx/cmake/stm32cubemx/CMakeLists.txtfirmware/c_board/bsp/cubemx/rmcs_slave.iocfirmware/rmcs_board/app/src/app.cppfirmware/rmcs_board/app/src/i2c/i2c.hppfirmware/rmcs_board/app/src/usb/vendor.hppfirmware/rmcs_board/bsp/hpm_sdkhost/include/librmcs/agent/c_board.hpphost/include/librmcs/agent/rmcs_board.hpphost/include/librmcs/protocol/handler.hpphost/src/protocol/handler.cpp
| @@ -1 +1 @@ | |||
| Subproject commit a82a53544d5679a43b70f390c8bbbb8e91f6b8c1 | |||
| Subproject commit 132349c9ddad3cbeca7211345d5d6086355f20b9 | |||
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Submodule pointer change =="
git diff --submodule=log origin/main...HEAD -- firmware/rmcs_board/bsp/hpm_sdk || true
echo
echo "== .gitmodules entry =="
sed -n '/firmware\/rmcs_board\/bsp\/hpm_sdk/,+5p' .gitmodules || true
echo
echo "== Old/New gitlinks =="
echo "[origin/main]"
git ls-tree origin/main -- firmware/rmcs_board/bsp/hpm_sdk || true
echo
echo "[HEAD]"
git ls-tree HEAD -- firmware/rmcs_board/bsp/hpm_sdk || trueRepository: Alliance-Algorithm/librmcs
Length of output: 676
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Check if hpm_sdk submodule is initialized =="
if [ -d "firmware/rmcs_board/bsp/hpm_sdk/.git" ]; then
echo "✓ Submodule is initialized"
cd firmware/rmcs_board/bsp/hpm_sdk
echo
echo "== Commit log between old and new commit =="
git log --oneline a82a53544d5679a43b70f390c8bbbb8e91f6b8c1..132349c9ddad3cbeca7211345d5d6086355f20b9 || true
echo
echo "== Detailed commit info for new commit =="
git show --stat 132349c9ddad3cbeca7211345d5d6086355f20b9 | head -100 || true
else
echo "✗ Submodule not initialized locally"
echo "Submodule path: firmware/rmcs_board/bsp/hpm_sdk"
fiRepository: Alliance-Algorithm/librmcs
Length of output: 201
🌐 Web query:
site:github.com/Alliance-Algorithm/hpm_sdk compare a82a53544d5679a43b70f390c8bbbb8e91f6b8c1...132349c9ddad3cbeca7211345d5d6086355f20b9
💡 Result:
GitHub shows no changes to compare between these commits: a82a5354... already contains all commits from 132349c9... (i.e., 132349c9... is behind / an ancestor of a82a5354...). (github.com)
If you want to see the reverse difference, switch the base/head (compare 132349c9... as base against a82a5354... as head). (github.com)
Citations:
🌐 Web query:
site:github.com/Alliance-Algorithm/hpm_sdk compare 132349c9ddad3cbeca7211345d5d6086355f20b9...a82a53544d5679a43b70f390c8bbbb8e91f6b8c1
💡 Result:
Comparison 132349c9ddad3cbeca7211345d5d6086355f20b9...a82a53544d5679a43b70f390c8bbbb8e91f6b8c1 in Alliance-Algorithm/hpm_sdk contains 1 commit (“iter1”, committed Mar 31, 2026 by qzhhhi), touching 2 files with +13 / -3 lines. GitHub couldn’t render the full compare view (“taking too long to generate”), but the commit diff is visible. [1][2]
Changes:
boards/hpm5300evk/board.c: adds a new functionboard_init_gpio_pins()that sets the app GPIO pin as input viagpio_set_pin_input(...). [2]boards/hpm5300evk/board.h:- adds board flash/XPI NOR related defines:
BOARD_FLASH_BASE_ADDRESS,BOARD_FLASH_SIZE,BOARD_APP_XPI_NOR_XPI_BASE, andBOARD_APP_XPI_NOR_CFG_OPT_*. [2] - changes the
board_init_gpio_pinsdeclaration from commented-out to enabled (void board_init_gpio_pins(void);). [2]
- adds board flash/XPI NOR related defines:
Sources: [1] compare view, [2] commit a82a535 diff.
这是版本回退,移除了 GPIO 初始化代码——需要明确说明回退原因和兼容性影响。
这次变更并非升级,而是从 a82a5354 回退到其祖先 132349c9。被回退的 commit(2026-03-31)移除了:
board_init_gpio_pins()函数及其 GPIO 初始化逻辑- Flash/XPI NOR 相关的 board 配置定义
当前 PR 直接依赖 HAL I2C 传输,如果 I2C 初始化或数据线依赖于 board_init_gpio_pins() 的 GPIO 状态配置,这个回退可能导致 I2C 传输失败。PR 缺少:
- 说明为什么需要此次回退
- GPIO 初始化被移除后的兼容性验证
- I2C/中断处理与 GPIO 初始化间的依赖关系说明
请补充这些信息或在本地完整验证后再提交。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@firmware/rmcs_board/bsp/hpm_sdk` at line 1, 回退移除了 GPIO
初始化(board_init_gpio_pins)和 Flash/XPI 配置,可能破坏依赖于 GPIO 状态的 HAL I2C 传输;请在 PR
中补充回退原因与兼容性评估、并在代码里恢复或有条件调用 board_init_gpio_pins()(或提供替代初始化路径)以保证 I2C
初始化/传输和中断处理行为一致;在本地或 CI 运行覆盖性验证:1) 验证 HAL I2C init/transfer 在无
board_init_gpio_pins 情况下是否失败;2) 验证中断和 I/O 线路的电平/复用设置;3) 恢复或迁移 Flash/XPI NOR
board 配置并记录任何行为差异;在 PR 描述中列出对比 a82a5354 与 132349c9
的差异、测试结果和最终决定(恢复、条件初始化或明确不兼容)。
There was a problem hiding this comment.
🧹 Nitpick comments (2)
firmware/c_board/bsp/cubemx/Core/Src/dma.c (1)
47-61: 建议将新增段落调整为 4 空格缩进以符合仓库规范。当前新增 NVIC 配置仍是 2 空格缩进;建议在本次改动中统一为 4 空格,避免后续格式漂移。
As per coding guidelines "Use 4 space indentation with 100 column width limit".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@firmware/c_board/bsp/cubemx/Core/Src/dma.c` around lines 47 - 61, The newly added NVIC configuration lines use 2-space indentation; update the block so each line is indented with 4 spaces to match the repository style. Locate the calls to HAL_NVIC_SetPriority and HAL_NVIC_EnableIRQ (e.g., for DMA1_Stream1_IRQn, DMA1_Stream2_IRQn, DMA1_Stream3_IRQn, DMA1_Stream7_IRQn, DMA2_Stream0_IRQn) in dma.c and reformat those lines to use 4-space leading indentation consistently.firmware/rmcs_board/app/src/i2c/i2c.hpp (1)
41-51: 为 HAL I2C 写接口参数添加注释说明,或考虑使用可写缓冲区副本。
payload来自std::span<const std::byte>(只读),通过const_cast传给i2c_master_write()和i2c_master_address_write()时,隐含依赖 HPM I2C 驱动"不会修改发送缓冲区"的契约。HPM SDK 的 API 设计(使用uint8_t*而非const uint8_t*)导致这种类型转换必要,但缺乏文档保证。建议:(1) 参考interrupt_safe_buffer.hpp:99的模式,添加NOLINT和注释说明原因,或 (2) 改为拷贝到内部可写缓冲区再调用驱动。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@firmware/rmcs_board/app/src/i2c/i2c.hpp` around lines 41 - 51, The code const_casts a read-only payload (payload from std::span<const std::byte>) and passes it to i2c_master_write / i2c_master_address_write, relying on an undocumented driver promise not to modify the buffer; fix by either (A) making the contract explicit: add a clear comment above the const_cast referencing payload, i2c_master_write, and i2c_master_address_write and add an appropriate NOLINT suppression exactly where const_cast is used, or (B) eliminate the const_cast by copying the payload into a local writable buffer (e.g., std::vector<uint8_t> or an interrupt-safe buffer) and pass that buffer's data to i2c_master_write / i2c_master_address_write; implement one of these two options and ensure reg_address handling stays the same when calling i2c_master_address_write.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@firmware/c_board/bsp/cubemx/Core/Src/dma.c`:
- Around line 47-61: The newly added NVIC configuration lines use 2-space
indentation; update the block so each line is indented with 4 spaces to match
the repository style. Locate the calls to HAL_NVIC_SetPriority and
HAL_NVIC_EnableIRQ (e.g., for DMA1_Stream1_IRQn, DMA1_Stream2_IRQn,
DMA1_Stream3_IRQn, DMA1_Stream7_IRQn, DMA2_Stream0_IRQn) in dma.c and reformat
those lines to use 4-space leading indentation consistently.
In `@firmware/rmcs_board/app/src/i2c/i2c.hpp`:
- Around line 41-51: The code const_casts a read-only payload (payload from
std::span<const std::byte>) and passes it to i2c_master_write /
i2c_master_address_write, relying on an undocumented driver promise not to
modify the buffer; fix by either (A) making the contract explicit: add a clear
comment above the const_cast referencing payload, i2c_master_write, and
i2c_master_address_write and add an appropriate NOLINT suppression exactly where
const_cast is used, or (B) eliminate the const_cast by copying the payload into
a local writable buffer (e.g., std::vector<uint8_t> or an interrupt-safe buffer)
and pass that buffer's data to i2c_master_write / i2c_master_address_write;
implement one of these two options and ensure reg_address handling stays the
same when calling i2c_master_address_write.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 6c4e3286-1e73-4326-aada-8cad3be0ded6
📒 Files selected for processing (9)
firmware/c_board/app/src/i2c/i2c.cppfirmware/c_board/app/src/i2c/i2c.hppfirmware/c_board/bsp/cubemx/Core/Inc/i2c.hfirmware/c_board/bsp/cubemx/Core/Inc/stm32f4xx_it.hfirmware/c_board/bsp/cubemx/Core/Src/dma.cfirmware/c_board/bsp/cubemx/Core/Src/i2c.cfirmware/c_board/bsp/cubemx/Core/Src/stm32f4xx_it.cfirmware/c_board/bsp/cubemx/rmcs_slave.iocfirmware/rmcs_board/app/src/i2c/i2c.hpp
🚧 Files skipped from review as they are similar to previous changes (5)
- firmware/c_board/bsp/cubemx/Core/Inc/i2c.h
- firmware/c_board/bsp/cubemx/Core/Src/stm32f4xx_it.c
- firmware/c_board/bsp/cubemx/Core/Inc/stm32f4xx_it.h
- firmware/c_board/bsp/cubemx/Core/Src/i2c.c
- firmware/c_board/bsp/cubemx/rmcs_slave.ioc
This PR adds end-to-end I2C support across the protocol, host SDK, and firmware.
It starts by introducing the I2C protocol and
RmcsBoardsupport, then addsc_boardI2C2 transport support, and finally improvesc_boardreliability by replacing the previous queued interrupt-driven implementation with blocking HAL I2C transfers.
The main motivation for the last change is to avoid internal queue-overflow failures during bursty workloads such as OLED initialization and full-screen updates.
Included Changes
RmcsBoardrmcs_boardCBoardc_boardc_boardCubeMX I2C and interrupt plumbingc_boardwith blocking HAL I2C operationsI2C 支持端到端实现与 c_board 传输稳定化(更新)
概览
本 PR 在协议、主机 SDK 与两块固件(c_board、rmcs_board)中实现了端到端的 I2C 支持,并通过将 c_board 的异步队列式 I2C 处理替换为基于 HAL 的阻塞/同步传输并加入 DMA 超时恢复逻辑,提升了在突发/高并发工作负载(例如 OLED 初始化与整屏更新)下的可靠性。保留主机端提供的从机地址为 7 位约定。
协议与数据结构
序列化与反序列化
c_board 固件(STM32F4)— 主要更改与稳健性改进
rmcs_board 固件(HPM SDK)
主机端(host)API 与协议层
关键设计决策与注意点
影响范围 & 兼容性